fix: [SDK-4852] harden iOS click listener registration to match Android pattern#883
Conversation
|
@claude review once |
There was a problem hiding this comment.
LGTM — mechanical mirror of the reviewed Android hardening from #881.
Extended reasoning...
Overview
Single-file change to com.onesignal.unity.ios/Runtime/iOSNotificationsManager.cs that applies the same three safeguards to the iOS Clicked event accessor that PR #881 applied to the Android bridge: a _clickRegistrationLock around both add/remove, setting _clickNativeListenerSet = true only after the native P/Invoke returns successfully, a try/catch rollback of _clicked -= value on native-call failure, and a null-handler guard in _onClicked before dispatching to the Unity main thread.
Security risks
None. This is a thread-safety and error-handling hardening on the notification click-event registration path. No auth, crypto, permissions, network I/O, or data-handling code is touched. The public API surface is unchanged.
Level of scrutiny
Low. The change is a line-for-line port of an already-reviewed and merged pattern (confirmed by grepping AndroidNotificationsManager.cs — structure, lock placement, rollback semantics, and null-guard all match). Success-path timing of lazy registration is unchanged, and the failure paths being hardened (concurrent first subscription, native call throwing, click arriving after unsubscribe) are not reachable in normal operation.
Other factors
The bug hunting system found no issues. The comment explaining the lazy-registration rationale is preserved. Manual testing is called out as in-progress in the PR description, but given the symmetry with a manually verified Android implementation and the lack of any behavioral change on the success path, this does not warrant blocking on human review.
Description
One Line Summary
Apply the same click-listener registration safeguards added to the Android bridge in #881 to the iOS bridge, so both platforms share the identical pattern.
Details
Motivation
#881 hardened the Android
Clickedevent accessor based on review feedback. The iOS bridge (iOSNotificationsManager) already used lazy native registration (which is why iOS never had the cold-start click drop), but had none of the safeguards:add/removeaccessors were unsynchronized, so two threads subscribing concurrently could both pass the_clickNativeListenerSetcheck and register the native callback twice._clickNativeListenerSet = truewas set before the native call, so a transient native-call failure left the flag stuck true with no listener registered — clicks silently lost for the app's lifetime with no retry.This PR applies the identical fixes: a private lock around both accessors, the flag set only after the native call returns, and a rollback (
_clicked -= value) before rethrowing on failure.It also adds the null-handler guard in
_onClickedthat the Android bridge already has — without it, a click arriving after all handlers are removed (the native listener is never unregistered) would throw aNullReferenceExceptionon the Unity main thread.Scope
iOS only, notification click events only. No public API changes and no behavior change on any success path — the lazy registration timing is exactly as before. Android is untouched (already done in #881).
Testing
Unit testing
None added — the repo has no unit test infrastructure; this mirrors the reviewed and manually verified Android change from #881.
Manual testing
Verified with the demo app built from this branch (Unity 6000.3.6f1, IL2CPP) on an iPhone 16 Pro simulator (iOS 18.1), using
xcrun simctl pushwith a OneSignal-format payload containing additional data:Clicked +=subscription registers the native callback through the new locked accessor without issues.ForegroundWillDisplayfired for each pushed notification and the notification displayed.Clickedhandler, and the native SDK recorded the notification as a DIRECT influence.The failure-path changes (concurrent first subscription, native call throwing) are not reachable in a normal run; correctness argued by symmetry with the reviewed Android implementation from #881.
Affected code checklist
Checklist
Overview
Testing
Final pass